<?php

namespace App\Model;

use App\Constants\ErrorCode;
use App\Interface\Pay\AliPaymentStrategy;
use App\Services\AdapayService;
use App\Services\BankService;
use App\Services\Bolai\PackService;
use App\Services\BspayService;
use App\Services\DeviceService;
use App\Services\Iot\IotService1 as IotService;
use App\Services\PaymentStrategyFactory;
use Hyperf\Database\Model\Relations\BelongsTo;
use Hyperf\Database\Model\Relations\HasMany;
use Hyperf\Database\Model\Relations\HasOne;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Arr;
use Hyperf\Redis\Redis;
use Carbon\Carbon;
use Util\Common;
use function Symfony\Component\String\s;


class Order extends Model
{

    public bool $timestamps = true;
    protected ?string $table = 'orders';

    protected array $casts = [
        'start_at' => 'datetime',
        'stop_at' => 'datetime'
    ];
    protected array $guarded = [

    ];

    const STATUS_UNPAID = 0;
    const STATUS_PAID = 1;
    const STATUS_USING = 2;
    const STATUS_END = 3;
    const STATUS_FAILED = 4;
    const STATUS_REFUNDED = 5;
    const STATUS_PAY_TIMEOUT = 6;


    const WX_PAY = 1;
    const ALI_PAY = 2;
    const BALANCE = 3;
    const CARD = 4;
    const ADMIN = 5;
    const WHITE = 6;

    const STATUS_MAP = [
        self::STATUS_UNPAID => '未支付',
        self::STATUS_PAID => '已支付',
        self::STATUS_USING => '使用中',
        self::STATUS_END => '已完成',
        self::STATUS_FAILED => '失败',
        self::STATUS_REFUNDED => '已退款',
        self::STATUS_PAY_TIMEOUT => '支付超时'
    ];


    public function feedback()
    {
        return $this->hasOne(Feedback::class);
    }


    public function detail(): HasOne
    {
        return $this->hasOne(OrderDetail::class, 'number', 'number');
    }

    public function refund(): HasMany
    {
        return $this->hasMany(RefundLog::class, 'number', 'number');
    }

    public function station(): BelongsTo
    {
        return $this->belongsTo(Station::class, 'station_id', 'id');
    }

    public function charge(): BelongsTo
    {
        return $this->belongsTo(ChargeOption::class, 'charge_id', 'id');
    }

    public function device(): BelongsTo
    {
        return $this->belongsTo(Device::class, 'device_id', 'id');
    }


    public function distributor(): BelongsTo
    {
        return $this->belongsTo(Distributors::class, 'distributor_id', 'id');
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    public function couponRecord(): BelongsTo
    {
        return $this->belongsTo(CouponRecord::class, 'coupon_record_id', 'id');
    }


    //
    public function getDurationSeconds()
    {
        return Carbon::now()->timestamp - $this->created_at->timestamp;
    }

    /**检查是否抽单
     * @return bool
     */
    public function checkDraw(): bool
    {
        $cacheKey = 'DRAW_DISTRIBUTOR_' . $this->distributor->id;
        if ($this->distributor->draw_active) {
            if (redis()->exists($cacheKey)) {
                $drawCount = redis()->get($cacheKey);
                if ($drawCount >= $this->distributor->draw_space) {
                    return true;
                }
            } else {
                redis()->set($cacheKey, 0);
                return false;
            }
        }
        return false;
    }

    public function incrDrawNumber()
    {
        $cacheKey = 'DRAW_DISTRIBUTOR_' . $this->distributor_id;
        redis()->incr($cacheKey);
    }

    public function drawOrder()
    {
        $cacheKey = 'DRAW_DISTRIBUTOR_' . $this->distributor_id;
        $this->is_draw = 1;
        redis()->del($cacheKey);
    }

    public function offStop($reason)
    {
        $this->status = Order::STATUS_END;
        $this->stop_at = Carbon::now();
        $this->stop_reason = $reason;
        $real_fee = $this->fee - $this->service_fee;
        if ($real_fee > 0) {
            if ($this->station->is_refund) {
                if ($this->detail) {
                    $return_fee = $real_fee - $this->detail->use_fee - $this->detail->charge_service;
                } else {
                    $return_fee = $real_fee;
                }
                if ($return_fee < 0) {
                    $return_fee = 0;
                }
                $real_fee -= $return_fee;
                if ($return_fee > 0) {
                    $this->refundOrder($return_fee);
                }
            }
            if ($real_fee) {
                $bool = $this->checkDraw();
                $this->incrDrawNumber();
                if ($bool) {
                    $this->drawOrder();
                }
                $this->addBill($real_fee);
            }
        }
        $this->save();
    }

    //是否需要通知设备停止
    public function stop($data)
    {
        $this->status = Order::STATUS_END;
        $this->stop_at = Carbon::now();
        if(!$this->stop_reason){
        $this->stop_reason = $data['stop_reason_text'];
        }
        if($data['stop_reason_text'] == '充电端口未充电'){
            $this->save();
            return '';
        }
        $real_fee = $this->fee - $this->service_fee;
        if ($real_fee > 0) {
            if ($this->station->is_refund) {
                if ($this->detail) {
                    if(isset($data['duration'])){
                        $this->detail->duration = $data['duration'];
                    }
                    if(isset($data['elec'])){
                        $this->detail->elec = $data['elec'];
                    }
                    if(isset($data['power'])){
                        $this->detail->power = $data['power'];
                    }
                    $this->detail->save();
                    if ($this->charge->min_price >$this->detail->use_fee + $this->detail->charge_service){
                        $return_fee = $real_fee - $this->charge->min_price;
                    }else{
                        $return_fee = $real_fee - $this->detail->use_fee - $this->detail->charge_service;
                    }
                } else {
                    $return_fee = $real_fee;
                }

                if ($return_fee < 0) {
                    $return_fee = 0;
                }
                $real_fee -= $return_fee;
                if ($return_fee > 0) {
                    $this->refundOrder($return_fee);
                }
            }
            if ($real_fee) {
//                $bool = $this->checkDraw();
//                $this->incrDrawNumber();
//                if ($bool) {
//                    $this->drawOrder();
//                }
                $this->addBill($real_fee);
            }
        }
        $this->save();
    }

    public function addBill($fee)
    {
        if ($this->payment_type == self::BALANCE) {
            $this->user->balance -= $fee;
            $this->user->save();
            $bill = new Bill();
            $bill->account_id = $this->user->id;
            $bill->account_type = 2;
            $bill->number = $this->number;
            $bill->fee = -$fee;
            $bill->payment_type = $this->payment_type;
            $bill->balance = $this->user->balance;
            $bill->target_id;
            $bill->save();
        }
        if (!$this->is_draw && $this->distributor) {
            $distributorFee = number_format($this->distributor->rate / 100 * $fee, 2, '.', '');
            $this->distributor->balance += $distributorFee;
            $this->distributor->save();
            $bill = new Bill();
            $bill->account_id = $this->distributor->id;
            $bill->account_type = 1;
            $bill->number = $this->number;
            $bill->fee = $distributorFee;
            $bill->payment_type = $this->payment_type;
            $bill->balance = $this->distributor->balance;
            $bill->target_id = $this->id;
            $bill->save();
            $this->addStatic($distributorFee, $this->distributor->id);
            if ($this->distributor->level > 1 && $fee > $distributorFee) {
                $parent = Distributors::query()->where('id', $this->distributor->parent_id)->first();
                $parentFee = number_format(($parent->rate - $this->distributor->rate) / 100 * $fee, 2, '.', '');
                if ($parentFee > 0) {
                    $parent->balance += $parentFee;
                    $parent->save();
                    $bill = new Bill();
                    $bill->account_id = $parent->id;
                    $bill->account_type = 1;
                    $bill->number = $this->number;
                    $bill->fee = $parentFee;
                    $bill->payment_type = $this->payment_type;
                    $bill->balance = $parent->balance;
                    $bill->target_id = $this->id;
                    $bill->save();
                    $this->addStatic($parentFee, $parent->id);
                }
            }
        }
    }

    public function addStatic($fee, $distributorId)
    {
        // 生成key：日期 + 分销商ID
        $key = 'ORDER_' . date('Y-m-d') . '_' . $distributorId;
        // 使用事务确保原子性
        redis()->multi();
        redis()->incrByFloat($key, (float)$fee);
        // 设置过期时间，如果key已存在且有过期时间，此操作不会影响原有过期时间
        redis()->expire($key, 86400);
        redis()->exec();
    }


    public function refundOrder($return_fee, $type = 1)
    {
        $normal = new Normal();
        $return_fee = number_format((float)$return_fee, 2, '.', '');
        if ($this->fee > 0) {
            $save['number'] = $this->number;
            $save['refund_no'] = 'RF' . $normal->randOrder($this->id);
            $save['status'] = 0;
            $save['reject_amt'] = $return_fee;
            $save['type'] = $this->payment_type;
            $save['pay_at'] = $this->pay_at;
            $save['refund_type'] = $type;
            $refund = RefundLog::create($save);
        }
        try {
            switch ($this->payment_type) {
                //支付宝支付退回
                case Order::WX_PAY:
                    //微信支付退回
                case Order::ALI_PAY:
                    $service = new BspayService();
                    $result = $service->refund($refund);
                    $refund->return_msg = json_encode($result);
                    $refund->save();
                    return $result;
                    break;
                case Order::BALANCE:
                    $this->return_fee += $return_fee;
                    $this->save();
                    $this->refund_at = Carbon::now();
                    $refund->status = 1;
                    $refund->save();
                    $refund->notifySomething();

                    break;
                default:
            }
        } catch (\Exception $e) {
            Normal::logger(__METHOD__.'退款代码-save-error--------' . $e->getMessage() . $e->getTraceAsString(), 'order-event');
        }
    }


    public function fail($reason)
    {
        $this->status = self::STATUS_FAILED;
        $this->stop_reason = $reason;
        $this->stop_at = Carbon::now();
        $this->save();
        try {
            $couponRecord = $this->couponRecord;
            $couponRecord && $couponRecord->markAsUnused();
            if ($this->payment_type < self::CARD) {
                $this->refundOrder($this->fee);
            }
            if($this->service_fee> 0){
                $this->user->refundService($this->service_fee);
            }
        } catch (\Exception $exception) {
            Normal::logger($this->number . '退款失败: ' . $exception->getMessage(), 'order-start');
        }
    }


    public function balancePay(array $extra = [])
    {
        $config = [
            'code' => 200,
            'need_pay' => false,
            'number' => $this->number
        ];
        if ($this->fee <= 0) {
            $this->paySuccess();
            return $config;
        }
        $user = $this->user;
        switch ($this->payment_type) {
            case self::BALANCE:
                if (!$user) {
                    return array('code' => 1001, 'msg' => '用户不存在');
                }
                if ($this->fee > $user->getTotalBalance()) {
                    return array('code' => 1001, 'msg' => '余额不足');
                }
                if ($this->service_fee) {
                    $this->user->upVip($this->service_fee);
                }
                $this->paySuccess();
                break;
            default:
        }
        return $config;
    }

    /**支付宝微信支付回调
     * @param array $params
     * @return void
     */
    public function payMent(array $params)
    {
        $date = \DateTime::createFromFormat('YmdHis', $params['end_time']);
        $this->pay_at = $date->format('Y-m-d H:i:s');
        $this->out_ord_id = $params['out_ord_id'];
        $this->status = Order::STATUS_PAID;
        $this->paySuccess();
        if ($this->service_fee) {
            $this->user->upVip($this->service_fee);
        }
    }

    public function paySuccess()
    {
        //尝试开始充电
        $this->status = Order::STATUS_PAID;
        $this->save();
        try {
            //有优惠卷就标记为已使用
            $couponRecord = $this->couponRecord;
            $couponRecord && $couponRecord->markAsUsed();
            $post = [
                'fd' => $this->device->fd,
                'sn' => $this->device->sn,
                'number' => $this->number,
                'channel_number' => $this->channel_number,
                'duration' => (int)($this->minutes * 60),
                'power_fee' => (int)($this->fee * 100)
            ];
            $response = DeviceService::postTcp('/tcp/start', $post);
            $responseData = json_decode($response->getBody()->getContents(), true);
            if ($response->getStatusCode() != 200 || $responseData['code'] != 200) {
                Normal::logger('设备启动命令响应 - 状态码: ' . $response->getStatusCode() .
                    ', 响应内容: ' . $response->getBody()->getContents() .
                    ', 订单号: ' . $this->number, 'order-event');
                $responseData = json_decode($response->getBody()->getContents(), true);
                // 记录解析后的响应数据
                Normal::logger('设备启动命令解析后数据: ' . json_encode($responseData, JSON_UNESCAPED_UNICODE), 'order-event');
                $this->fail('开启充电失败');
            }
        } catch (\Exception $exception) {
            $message = $exception->getMessage();
            Normal::logger('设备启动异常 - 订单号: ' . $this->number .
                ', 错误信息: ' . $message .
                ', Trace: ' . $exception->getTraceAsString(), 'order-event');
            //标记为租用失败
            $this->fail($message);
        }
    }

    public function checkCanRefund()
    {
        //完成的订单和失败的才能退款
        if (!in_array($this->status, [
            self::STATUS_FAILED,
            self::STATUS_END,
            self::STATUS_PAID
        ])) {
            return false;
        }

        //有用户的订单都表示是线上支付的 就可以退
        if (!$this->user) {
            return false;
        }

        return true;
    }


    /**自定义退款
     * @param $params
     * @return void
     */
    public function normalRefundSuccess($params)
    {
        $fee = $params['fee'];
        $deviceModel = Device::where('id', $this->device_id)->first();
        $firstDis = Distributors::where('id', $deviceModel->distributor_id)
            ->first();
        if ($deviceModel->child_distributor_id) {
            $childDis = Distributors::where('id', $deviceModel->child_distributor_id)
                ->first();
            $childDis->balance -= $fee;
            $childDis->save();
            $distributorDevice = DistributorDevice::where('distributor_id', $childDis->id)
                ->where('device_id', $this->device_id)
                ->first();
            if ($distributorDevice) {
                $distributorDevice->profit -= $fee;
                $distributorDevice->save();
            } else {
                $profit['profit'] = -$fee;
                $profit['distributor_id'] = $childDis->id;
                $profit['device_id'] = $this->device_id;
                DistributorDevice::create($profit);
            }
            $bills = Bill::where('target_id', $this->id)
                ->where('account_id', $deviceModel->child_distributor_id)
                ->first();
            if ($bills) {
                $bills->fee -= $fee;
                $bills->balance -= $fee;
                $bills->save();
            }
        } else {
            $firstMoney = $fee;
            $firstDis->balance -= $firstMoney;
            $firstDis->save();
            $distributorDevice = DistributorDevice::where('distributor_id', $firstDis->id)
                ->where('device_id', $this->device_id)
                ->first();
            if ($distributorDevice) {
                $distributorDevice->profit -= $firstMoney;
                $distributorDevice->save();
            } else {
                $profit['profit'] = -$firstMoney;
                $profit['distributor_id'] = $firstDis->id;
                $profit['device_id'] = $this->device_id;
                DistributorDevice::create($profit);
            }
            $bills = Bill::where('target_id', $this->id)
                ->where('account_id', $deviceModel->distributor_id)
                ->first();
            if ($bills) {
                $bills->fee -= $firstMoney;
                $bills->balance -= $firstMoney;
                $bills->save();
            }
        }
    }

    /**自定义退款操作
     * @param $fee
     * @return string|void
     */
    public function normalRefund($fee)
    {
        if ($this->status != self::STATUS_END && $this->status != self::STATUS_REFUNDED) {
            return '';
        }
        switch ($this->payment_type) {
            //支付宝支付退回
            case Bill::PAYMENT_TYPE_ALI_PAY:
                //微信支付退回
            case Bill::PAYMENT_TYPE_WE_CHAT_PAY:
                $refoundParams = array(
                    "payment_id" => $this->payment_id,
                    # 退款订单号
                    "refund_order_no" => 'NR' . rand(99, 999) . $this->number,
                    # 退款金额
                    "refund_amt" => $fee,
                    "notify_url" => Common::MAIN_HOST . '/user/order/refund_normal'
                );
                $adapayService = new AdapayService();
                $adapayService->refundPayment($refoundParams);
                break;
            default:
                $this->return_fee += $fee;
                $this->save();
                $this->user->balance += $fee;
                $this->user->save();
                break;
        }
    }


    /**退款分账流水
     * @param $params
     * @return void
     */
    public function refundSuccess($params)
    {
        $this->return_fee += $params['actual_ref_amt'];
        $this->save();
    }

    /**经销商盈利
     * @return void
     */
    public function distributorSettle($return_fee)
    {
        try {
            $orderMoney = $this->charge_service;
            if ($orderMoney <= 0) {
                return '';
            }
            $deviceModel = Device::where('id', $this->device_id)->first();
            $firstDis = Distributors::where('id', $deviceModel->distributor_id)
                ->first();
            if ($deviceModel->child_distributor_id) {
                $childDis = Distributors::where('id', $deviceModel->child_distributor_id)
                    ->first();
                $disMoney = number_format($orderMoney * $childDis->rate / 100, 2);
                $childDis->balance += $disMoney;
                $childDis->save();
                $distributorDevice = DistributorDevice::where('distributor_id', $childDis->id)
                    ->where('device_id', $this->device_id)
                    ->first();
                if ($distributorDevice) {
                    $distributorDevice->profit += $disMoney;
                    $distributorDevice->save();
                } else {
                    $profit['profit'] = $disMoney;
                    $profit['distributor_id'] = $childDis->id;
                    $profit['device_id'] = $this->device_id;
                    DistributorDevice::create($profit);
                }
                Bill::record(
                    $childDis,
                    $disMoney,
                    Bill::TYPE_ORDER_PAY,
                    $this->payment_type,
                    $this->id
                );
                if ($firstDis->rate > $childDis->rate && $this->is_disabled != 1) {
                    $firstRate = $firstDis->rate - $childDis->rate;
                    if ($firstRate > 0) {
                        $firstMoney = number_format($orderMoney * $firstRate / 100, 2);
                        $firstDis->balance += $firstMoney;
                        $firstDis->save();
                        $distributorDevice = DistributorDevice::where('distributor_id', $firstDis->id)
                            ->where('device_id', $this->device_id)
                            ->first();
                        if ($distributorDevice) {
                            $distributorDevice->profit += $firstMoney;
                            $distributorDevice->save();
                        } else {
                            $profit['profit'] = $firstMoney;
                            $profit['distributor_id'] = $firstDis->id;
                            $profit['device_id'] = $this->device_id;
                            DistributorDevice::create($profit);
                        }
                        Bill::record(
                            $firstDis,
                            $firstMoney,
                            Bill::TYPE_ORDER_PAY,
                            $this->payment_type,
                            $this->id
                        );
                    }
                }
            } else {
                $firstMoney = number_format($orderMoney * $firstDis->rate / 100, 2);
                $firstDis->balance += $firstMoney;
                $firstDis->save();
                $distributorDevice = DistributorDevice::where('distributor_id', $firstDis->id)
                    ->where('device_id', $this->device_id)
                    ->first();
                if ($distributorDevice) {
                    $distributorDevice->profit += $firstMoney;
                    $distributorDevice->save();
                } else {
                    $profit['profit'] = $firstMoney;
                    $profit['distributor_id'] = $firstDis->id;
                    $profit['device_id'] = $this->device_id;
                    DistributorDevice::create($profit);
                }
                Bill::record(
                    $firstDis,
                    $firstMoney,
                    Bill::TYPE_ORDER_PAY,
                    $this->payment_type,
                    $this->id
                );
            }
        } catch (\Exception $e) {
            echo $e->getMessage();
        }
    }


}
